package com.winai.launcher.manager;

import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.content.Context;
import android.text.TextUtils;
import android.util.Log;

import com.winai.launcher.bean.BleRssiDevice;
import com.winai.launcher.interfaces.ScanCallBack;
import com.winai.launcher.override.ToastKs;
import com.winai.launcher.utils.Utils;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Random;
import java.util.Set;
import java.util.UUID;

import cn.com.heaton.blelibrary.ble.Ble;
import cn.com.heaton.blelibrary.ble.BleLog;
import cn.com.heaton.blelibrary.ble.callback.BleConnectCallback;
import cn.com.heaton.blelibrary.ble.callback.BleNotiftCallback;
import cn.com.heaton.blelibrary.ble.callback.BleScanCallback;
import cn.com.heaton.blelibrary.ble.callback.BleWriteCallback;
import cn.com.heaton.blelibrary.ble.callback.BleWriteDescCallback;
import cn.com.heaton.blelibrary.ble.model.BleDevice;
import cn.com.heaton.blelibrary.ble.utils.ByteUtils;
import cn.com.heaton.blelibrary.ble.utils.ThreadUtils;

public class AysDeviceManager {
    private final String TAG = "AysDeviceManager";
    private Context context;
    private static AysDeviceManager instance;

    private Ble<BleDevice> ble;
    private ArrayList<BleRssiDevice> bleRssiDevices = new ArrayList<>();
//    private BleDevice connectDevice;
    //    private BluetoothGattService service;
    private ScanCallBack scanCallBack;
    private com.winai.launcher.interfaces.BleConnectCallback connectCallback;
    public String uuid = "000018bb-0000-1000-8000-00805f9b34fb";//写入服务的uuid
    private Set<String> DeviceKeySet = new HashSet<>();
    private ArrayList<String> DeviceAddressSet = new ArrayList<>();//单播地址

    public static AysDeviceManager getInstance(Context mContext) {
        if (instance == null) {
            synchronized (AysDeviceManager.class) {
                if (instance == null) {
                    instance = new AysDeviceManager(mContext.getApplicationContext());
                }
            }
        }
        return instance;
    }

    public AysDeviceManager(Context mContext) {
        this.context = mContext;
        ble = Ble.getInstance();
        //单播地址取值0003-7fff
        for (int i = 3; i < 0x7fff; i++) {
            String addr = String.format("%04x", i);
            String startStr = addr.substring(0, 2);
            String endStr = addr.substring(2);
            String s = endStr + startStr;
            DeviceAddressSet.add(s);
        }
    }

    private BleScanCallback<BleDevice> scanCallback = new BleScanCallback<BleDevice>() {
        @Override
        public void onLeScan(final BleDevice device, int rssi, byte[] scanRecord) {
            if (TextUtils.isEmpty(device.getBleAddress())) return;
            if (scanCallBack != null) {
                scanCallBack.onLeScan(device, rssi, scanRecord);
            }
        }

        @Override
        public void onStart() {
            super.onStart();
            Log.e(TAG, "onLeScan: onStart");
            if (scanCallBack != null) {
                scanCallBack.onStart();
            }
        }

        @Override
        public void onStop() {
            super.onStop();
            Log.e(TAG, "onLeScan: onStop");
            if (scanCallBack != null) {
                scanCallBack.onStop();
            }
        }
    };

    private BleConnectCallback<BleDevice> callback = new BleConnectCallback<BleDevice>() {
        @Override
        public void onConnectionChanged(BleDevice device) {
            BleLog.e(TAG, "onConnectionChanged: " + device.getConnectionState());
            if (device.isConnected()) {
//                connectDevice = device;
                ToastKs.show(context, "连接成功 " + device.getBleAddress());
            } else if (device.isConnectting()) {
            } else if (device.isDisconnected()) {
            }
            if (connectCallback != null) {
                connectCallback.onConnectionChanged(device);
            }
        }

        @Override
        public void onConnectException(BleDevice device, int errorCode) {
            super.onConnectException(device, errorCode);
            Utils.showToast("连接异常，异常状态码:" + errorCode);
            if (connectCallback != null) {
                connectCallback.onConnectException(device, errorCode);
            }
        }

        @Override
        public void onConnectTimeOut(BleDevice device) {
            super.onConnectTimeOut(device);
            BleLog.e(TAG, "onConnectTimeOut: " + device.getBleAddress());
            Utils.showToast("连接超时:" + device.getBleName());
            if (connectCallback != null) {
                connectCallback.onConnectTimeOut(device);
            }
        }

        @Override
        public void onConnectCancel(BleDevice device) {
            super.onConnectCancel(device);
            BleLog.e(TAG, "onConnectCancel: " + device.getBleName());
            if (connectCallback != null) {
                connectCallback.onConnectCancel(device);
            }
        }

        @Override
        public void onServicesDiscovered(BleDevice device, final List<BluetoothGattService> services) {
            super.onServicesDiscovered(device, services);
//            connectDevice = device;
            BleLog.e(TAG, "onServicesDiscovered: " + services.size());
            if (connectCallback != null) {
                connectCallback.onServicesDiscovered(device, services);
            }
        }

        @Override
        public void onReady(BleDevice device) {
            super.onReady(device);
            BleLog.e(TAG, "onReady: " + device.getBleAddress());
            if (connectCallback != null) {
                connectCallback.onReady(device);
            }
            //连接成功后，设置通知
//            ble.enableNotify(device, true, new BleNotiftCallback<BleDevice>() {
//                @Override
//                public void onChanged(BleDevice device, BluetoothGattCharacteristic characteristic) {
//                    UUID uuid = characteristic.getUuid();
//                    BleLog.e(TAG, "onChanged==uuid:" + uuid.toString());
//                    BleLog.e(TAG, "onChanged==data:" + ByteUtils.toHexString(characteristic.getValue()));
//                    new Handler(Looper.getMainLooper()).post(new Runnable() {
//                        @Override
//                        public void run() {
//                            Utils.showToast(String.format("收到设备通知数据: %s", ByteUtils.toHexString(characteristic.getValue())));
//                        }
//                    });
//                }
//
//                @Override
//                public void onNotifySuccess(BleDevice device) {
//                    super.onNotifySuccess(device);
//                }
//            });
        }
    };

    public void startScan(ScanCallBack cb) {
//        if (scanCallBack == null) {
        this.scanCallBack = cb;
//        }
        ble.startScan(scanCallback);
    }

    public void stopScan() {
        if (ble.isScanning()) {
            ble.stopScan();
        }
    }

    public Ble<BleDevice> getBle() {
        return ble;
    }

    public ArrayList<BleRssiDevice> getBleRssiDevices() {
        return bleRssiDevices;
    }

    public void startConnect(BleDevice device, com.winai.launcher.interfaces.BleConnectCallback bleConnectCallback) {
        if (bleConnectCallback != null) {
            this.connectCallback = bleConnectCallback;
        }
//        stopScan();
        if (device.isConnected()) {
            BleLog.e(TAG, "device.isConnected(): " + device.getBleName());
        } else if (device.isConnectting()) {
//            ble.cancelConnectting(device);
            BleLog.e(TAG, "device.isConnectting(): " + device.getBleName());
        } else if (device.isDisconnected()) {
            BleLog.e(TAG, "device.isDisconnected(): " + device.getBleName());
            boolean connect = ble.connect(device, callback);
//            if (connect) {
//                List<BluetoothGattService> gattServices = ble.getBleRequest().getSupportedGattServices(device.getBleAddress());
//                callback.onServicesDiscovered(device, gattServices);
//            }
        }
    }

    public void startConnect(String address, com.winai.launcher.interfaces.BleConnectCallback bleConnectCallback) {
        if (bleConnectCallback != null) {
            this.connectCallback = bleConnectCallback;
        }
        stopScan();
        ble.connect(address, callback);
    }

    public void openNotify(String deviceAddress, BleNotiftCallback<BleDevice> callback) {
        Log.e(TAG, deviceAddress + "打开Notify");
        BleDevice connectDevice = null;
        List<BleDevice> connetedDevices = Ble.getInstance().getConnetedDevices();
        for (BleDevice device : connetedDevices) {
            if (device.getBleAddress().equals(deviceAddress)) {
                connectDevice = device;
            }
        }
        if (connectDevice == null) {
            ThreadUtils.ui(new Runnable() {
                @Override
                public void run() {
                    ToastKs.show(context, "未连接此设备");
                }
            });
            return;
        }
        UUID characteristicUuid = null;
        BluetoothGattService bluetoothGattService = getBluetoothGattService(deviceAddress, uuid);
        if (bluetoothGattService == null) {
            ThreadUtils.ui(new Runnable() {
                @Override
                public void run() {
                    ToastKs.show(context, "未连接此设备");
                }
            });
            return;
        }
        for (BluetoothGattCharacteristic characteristic : bluetoothGattService.getCharacteristics()) {
            if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_NOTIFY) != 0
                    || (characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_INDICATE) != 0) {
                characteristicUuid = characteristic.getUuid();
            }
        }
        Ble.getInstance().enableNotifyByUuid(
                connectDevice,
                true,
                bluetoothGattService.getUuid(),
                characteristicUuid,
                callback);
    }

    public void sendWrite(String deviceAddress, String command, BleWriteCallback<BleDevice> callback) {
        Log.e(TAG, deviceAddress + "发送了一条消息：" + command);
        write(deviceAddress, null, ByteUtils.hexStr2Bytes(command.trim()), callback);
    }

    private void write(String deviceAddress, BluetoothGattDescriptor descriptor, byte[] bytes, BleWriteCallback<BleDevice> callback) {
        BluetoothGattService bluetoothGattService = getBluetoothGattService(deviceAddress, uuid);
        if (bluetoothGattService == null) {
            ThreadUtils.ui(new Runnable() {
                @Override
                public void run() {
                    ToastKs.show(context, "未连接此设备 1");
                }
            });
            return;
        }
        List<BleDevice> connetedDevices = Ble.getInstance().getConnetedDevices();
        UUID serviceUuid = bluetoothGattService.getUuid();
        UUID characteristicUuid = null;
        for (BluetoothGattCharacteristic characteristic : bluetoothGattService.getCharacteristics()) {
            if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) != 0
                    || (characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE) != 0) {
                characteristicUuid = characteristic.getUuid();
            }
        }

        if (!connetedDevices.isEmpty()) {
            BleDevice bleDevice = connetedDevices.get(0);
            if (descriptor == null) {
                writeChar(bleDevice, bytes, serviceUuid, characteristicUuid, callback);
            } else {
                writeDes(bleDevice, bytes, serviceUuid, characteristicUuid, descriptor);
            }
        }
    }

    private void writeChar(BleDevice bleDevice, byte[] bytes, UUID serviceUuid, UUID characteristicUuid, BleWriteCallback<BleDevice> callback) {
        if (callback != null) {
            Ble.getInstance().writeByUuid(
                    bleDevice,
                    bytes,
                    serviceUuid,
                    characteristicUuid,
                    callback);
        } else {
            Ble.getInstance().writeByUuid(
                    bleDevice,
                    bytes,
                    serviceUuid,
                    characteristicUuid
                    , new BleWriteCallback<BleDevice>() {
                        @Override
                        public void onWriteSuccess(BleDevice device, BluetoothGattCharacteristic characteristic) {
                            ThreadUtils.ui(new Runnable() {
                                @Override
                                public void run() {
                                    ToastKs.show(context, "操作成功");
                                }
                            });
                        }

                        @Override
                        public void onWiteFailed(BleDevice device, int failedCode) {
                            super.onWiteFailed(device, failedCode);
                            ThreadUtils.ui(new Runnable() {
                                @Override
                                public void run() {
                                    ToastKs.show(context, "操作失败:" + failedCode);
                                }
                            });
                        }
                    });

        }
    }

    private void writeDes(BleDevice bleDevice, byte[] bytes, UUID serviceUuid, UUID characteristicUuid, BluetoothGattDescriptor descriptor) {
        Ble.getInstance().writeDesByUuid(
                bleDevice,
                bytes,
                serviceUuid,
                characteristicUuid,
                descriptor.getUuid(),
                new BleWriteDescCallback<BleDevice>() {
                    @Override
                    public void onWriteDescSuccess(BleDevice dedvice, BluetoothGattDescriptor descriptor) {
                        super.onWriteDescSuccess(dedvice, descriptor);
                        ThreadUtils.ui(new Runnable() {
                            @Override
                            public void run() {
                                toast("写入描述成功");
                            }
                        });
                    }

                    @Override
                    public void onWriteDescFailed(BleDevice device, int failedCode) {
                        super.onWriteDescFailed(device, failedCode);
                        ThreadUtils.ui(new Runnable() {
                            @Override
                            public void run() {
                                toast("写入描述失败:" + failedCode);
                            }
                        });
                    }
                });
    }

    /**
     * @date 4/14/21
     * @desc 根据设备名称获取gattService；
     */
    public BluetoothGattService getBluetoothGattService(String deviceAddress, String uuid) {
        BluetoothGattService service = null;
        if (TextUtils.isEmpty(deviceAddress)) {
            return null;
        }
        List<BluetoothGattService> gattServices = ble.getBleRequest().getSupportedGattServices(deviceAddress);
        if (gattServices != null) {
            for (BluetoothGattService ss : gattServices) {
                if (uuid.equals(ss.getUuid().toString())) {
                    Log.e(TAG, "service.getUuid = " + ss.getUuid());
                    service = ss;
                }
            }
        }
        return service;
    }

    void toast(final String msg) {
        ThreadUtils.ui(new Runnable() {
            @Override
            public void run() {
                Utils.showToast(msg);
            }
        });
    }

    /**
     * @date 4/15/21
     * @desc 获取随机单播地址
     */
    public String getAddress() {
        int index = new Random().nextInt(DeviceAddressSet.size());
        String value = DeviceAddressSet.get(index);
        DeviceAddressSet.remove(index);
        return value;
    }

    /**
     * @date 4/14/21
     * @desc 获取DeviceKey
     */
    public String getDeviceKey() {
        String code = randomHexString(32);
        while (isDeviceKeyRepeat(code)) {
            code = randomHexString(32);
        }
        DeviceKeySet.add(code);
        return code;
    }

    private boolean isDeviceKeyRepeat(String code) {
        for (String ss : DeviceKeySet) {
            if (ss.equals(code)) {
                return true;
            }
        }
        return false;
    }

    private boolean isDeviceAddressRepeat(String code) {
        for (String ss : DeviceAddressSet) {
            if (ss.equals(code)) {
                return true;
            }
        }
        return false;
    }

    private String randomHexString(int len) {
        try {
            StringBuffer result = new StringBuffer();
            for (int i = 0; i < len; i++) {
                result.append(Integer.toHexString(new Random().nextInt(16)));
            }
            return result.toString().toUpperCase();
        } catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
        }
        return null;
    }
}
